//如果测试一直卡在MQTT连接上，请使用PubSubClient库的2.7版本（资源文件夹下有压缩包文件），目前测试最新版2.8无法连接
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

#define WIFI_SSID         "XXXXXXX"       //wifi名称
#define WIFI_PASSWORD     "XXXXXXX"       //wifi密码
#define device_id         "XXXXXXX"       //注册设备的ID和密钥
#define secret            "XXXXXXX"    
//MQTT三元组
#define ClientId          "XXXXXXX"
#define Username          "XXXXXXX"
#define Password          "XXXXXXX"

#define MQTT_Address "iot-mqtts.cn-north-4.myhuaweicloud.com"
#define MQTT_Port 1883
#define Iot_link_Body_Format "{\"services\":[{\"service_id\":\"你的服务ID\",\"properties\":{%s" //注意修改自己的服务ID
//{"services":[{"service_id":"Dev_data","properties":{"temp": 39}}]}
//设备属性上报
#define Iot_link_MQTT_Topic_Report "$oc/devices/"device_id"/sys/properties/report"
//接收平台下发的命令
#define Iot_link_MQTT_Topic_Commands "$oc/devices/"device_id"/sys/commands/#" 
//设备相应平台的命令
#define Iot_link_MQTT_Topic_CommandsRes "$oc/devices/"device_id"/sys/commands/response/request_id="
WiFiClient myesp8266Client;
PubSubClient myclient(myesp8266Client);
int data_temp=1;
long lastMsg = 0;

#define LEDPIN 0

void setup() {
  // put your setup code here, to run once:
  Serial.begin(19200);
  pinMode(LEDPIN,OUTPUT);
  digitalWrite(LEDPIN,HIGH);  //LED低电平触发,高电平熄灭
  WIFI_Init();
  MQTT_Init();
}
void loop() {
  // put your main code here, to run repeatedly:
  if (!myclient.connected()){
    MQTT_Init();
  } 
  else myclient.loop();
  long now = millis();
  if (now - lastMsg > 5000) {
    lastMsg = now;
    MQTT_POST();
    data_temp++;  //模拟温度值
  }
}
//连接wifi
void WIFI_Init()
{
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID,WIFI_PASSWORD);
    while(WiFi.status()!=WL_CONNECTED)
    {
      delay(1000);
      Serial.println("WiFi Not Connect");
    }
    Serial.println("WiFi Connected OK!");
}
//连接MQTT
void MQTT_Init()
{
  myclient.setServer(MQTT_Address,MQTT_Port);                 
  while(!myclient.connected())
  {
    myclient.connect(ClientId,Username,Password);
  }
   myclient.setCallback(callback);//设定回调方式，当ESP8266收到订阅消息时会调用此方法
  boolean res = myclient.subscribe(Iot_link_MQTT_Topic_Commands); //连接成功时订阅主题commands
  if(res != true){
     Serial.printf("mqtt subscribe topic [%s] fail\n", Iot_link_MQTT_Topic_Commands);
  }
  Serial.printf("mqtt subscribe topic [%s] ok\n", Iot_link_MQTT_Topic_Commands);

  Serial.println("MQTT Connected OK!");

}
void MQTT_POST()
{
  char properties[32];
  char jsonBuf[128];
  sprintf(properties,"\"temp\":%d}}]}",data_temp);
  sprintf(jsonBuf,Iot_link_Body_Format,properties);
  myclient.publish(Iot_link_MQTT_Topic_Report, jsonBuf);
  Serial.println(Iot_link_MQTT_Topic_Report);
  Serial.println(jsonBuf);
  Serial.println("MQTT Publish OK!");
}

void callback(char* topic, byte* payload, unsigned int length)
{
  String recdata="";
  Serial.printf("接收到订阅的消息:主题为：");
  Serial.println(topic); 
  Serial.printf("数据内容：");
  for(int i=0;i<length;i++)
  {
    recdata+=(char)payload[i];
  }
  Serial.println(recdata);
  //解析JSON数据
  DynamicJsonDocument jsonBuffer(1024);
  deserializeJson(jsonBuffer,recdata);
  JsonObject obj = jsonBuffer.as<JsonObject>();
  String com = obj["paras"];
  Serial.printf("解析命令:");
  Serial.println(com);
  deserializeJson(jsonBuffer,com);
  obj = jsonBuffer.as<JsonObject>();
  String ledcom = obj["led"];
  Serial.printf("解析LED命令:");
  Serial.println(ledcom);
  //解析request id，设备响应时的topic需要包含命令的request id，且会动态变化
  char *p=topic;
  String request_id="";
  int i=0;
  while((*p++)!='\0')//获取topic长度
  {
    i++;
  }
  topic+=(i-36);    //移动指针位置
  for(int j=i-36;j<i;j++)//末36位为request id
    request_id+=*topic++;
  Serial.println("request_id："+request_id);
  Serial.println("命令设备响应");
  String param="{}";
  myclient.publish((Iot_link_MQTT_Topic_CommandsRes+request_id).c_str(),param.c_str());
  if(ledcom=="0")
  {
    digitalWrite(LEDPIN,HIGH);  //LED低电平触发,高电平熄灭
    Serial.println("关灯");  
  }
  else if(ledcom=="1")
  {
    digitalWrite(LEDPIN,LOW);  //LED低电平触发,高电平熄灭
    Serial.println("开灯");    
  }  
}
